import java.io.IOException;
import java.io.Reader;
import java.util.Iterator;
import java.util.NoSuchElementException;

public class DocumentIterator implements Iterator<String> {

    private Reader r;
    private int c = -1;
    // n for n-gram
    private int n;
    // cache to store read words
    private String[] buffer;
    // whether initialized
    private boolean initialized = false;


    public DocumentIterator(Reader r, int n) {
        this.r = r;
        this.n = n;
        this.buffer = new String[n];
        skipNonLetters();
    }


    private void skipNonLetters() {
        try {
            do {
                this.c = this.r.read();
            } while (!Character.isLetter(this.c) && this.c != -1);
        } catch (IOException e) {
            this.c = -1;
        }
    }


    private void init() {
        if (this.initialized) {
            return;
        }

        int index = 0;

        // loop to fill the buffer with the first (n-1) words
        while (index < n - 1) {
            String word = readNextWord();
            if (word == null) {
                break;
            }
            buffer[index++] = word;
        }

        this.initialized = true;
    }

    /**
     * read and return the next word, ignore non-letter characters
     * @return next word or null if none found
     */
    private String readNextWord() {
        StringBuilder word = new StringBuilder();
        try {
            // stop at non-word characters
            while (Character.isLetter(c)) {
                word.append((char) c);
                c = r.read();
            }
            skipNonLetters();
        } catch (IOException e) {
            c = -1;
        }
        return word.length() > 0 ? word.toString() : null;
    }


    @Override
    public boolean hasNext() {
        return (c != -1);
    }


    @Override
    public String next() {

        // no more words, throw exception
        if (!hasNext()) {
            throw new NoSuchElementException();
        }

        // init buffer if needed
        if (!initialized) {
            init();
        }

        // fill buffer with the next word
        String last = this.readNextWord();
        this.buffer[n - 1] = last;

        // build the result
        StringBuilder answer = new StringBuilder();
        for (int i = 0; i < n; i++) {
            answer.append(buffer[i] == null ? "" : buffer[i]);
        }

        // shift the buffer (sliding window)
        System.arraycopy(buffer, 1, buffer, 0, n - 1);
        return answer
                .toString()
                .toLowerCase();
    }

}
